package source.view;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import source.events.action.MoveDirection;
import source.model.Base;
import source.model.DataHandler;
import source.model.GameObject;
import source.model.ObjectID;
import source.model.Player;
import source.model.Position;
import source.model.Unit;
import source.model.type.GameObjectType;
import source.view.viewobjects.ViewObject;
import source.view.viewobjects.ViewObjectWithStats;
import source.view.viewobjects.ViewTerrain;
import source.view.viewobjects.ViewUnit;

public class ViewData {
	private Hashtable<ViewObject, Position> viewObjectGrid;
	private Hashtable<ViewTerrain, Position> terrainGrid;
	private Position selectedTile;
	private List<Position> highlightedTiles;
	private ViewObjectWithStats currentViewObject;
	private int currentPlayer;
	private static ArrayList<ObjectID> objectIDS;
	
	private DataHandler dataHandler;
	
	public ViewData(DataHandler dataHandler){
		viewObjectGrid = new Hashtable<ViewObject, Position>();
		terrainGrid = new Hashtable<ViewTerrain, Position>();
		
		selectedTile = new Position(0,0);
		highlightedTiles = new ArrayList<Position>();
		
		this.dataHandler = dataHandler;
		objectIDS = new ArrayList<ObjectID>();
	}
	
	public Vector<Unit> getUnitsForPlayer(){
		return dataHandler.getPlayersUnits(currentPlayer);
	}
	/**
	 * Get the current players stats.
	 * @return
	 */
	public Hashtable<String, String> getPlayerStats(){
		Player player = dataHandler.getPlayer(currentPlayer);
		return player.getViewData();
	}
	/**
	 * Sets the currentplayer number.
	 * @param id
	 */
	public void setCurrentPlayer(int id){
		currentPlayer = id;
	}
	
	/**
	 * Returns the current player's id.
	 * @return
	 */
	public int getCurrentPlayer(){
		return currentPlayer;
	}
	
	public source.model.Player getCurrentPlayerObject()
	{
		return dataHandler.getPlayer(currentPlayer);
	}
	
	/**
	 * Sets the current viewable object.
	 * Preconditions: this is a selectable object.
	 * @param currentViewObject
	 */
	public void setCurrentViewObject(ViewObjectWithStats currentViewObject){
		this.currentViewObject = currentViewObject;
	}
	
	/**
	 * get the currently selected view object.
	 */
	public ViewObjectWithStats getCurrentViewObject(){
		return this.currentViewObject;
	}
	
	/**
	 * Add the ViewObject to the specified Position.
	 * @param vobj
	 * @param p
	 */
	public void add(ViewObject vobj, Position p){
		if(viewObjectGrid.containsKey(vobj)){
			MainScreen.writeToConsole("View Map error: Multiple instances of same object trying to appearing on screen");
		}
		else{
			viewObjectGrid.put(vobj, p);
		}
	}
	
	/**
	 * Adds a terrain to the specified position.
	 * @param vobj
	 * @param p
	 */
	public void addTerrain(ViewTerrain vobj, Position p){
		terrainGrid.put(vobj, p);
	}
	
	/**
	 * Move the ViewObject to the specified position.
	 * @param vobj
	 * @param pos
	 */
	public void move(ViewObject vobj, Position pos){
		viewObjectGrid.remove(vobj);
		viewObjectGrid.put(vobj, pos);
	}
	
	/**
	 * Move the ViewObject with the given ObjectID to the specified position.
	 * 
	 * precondition: oid is a valid ObjectID, pos is a valid Position
	 * postcondition: the ViewObject with ObjectID is moved to the Position supplied
	 * 
	 * @param oid
	 * @param pos
	 * @return true if the move was sucessful, false if it fails.
	 */
	public boolean move(ObjectID oid, Position pos)
	{
		int prefix = oid.getTypePrefix();
		GameObjectType thisType = null;
		
		for ( GameObjectType got : GameObjectType.values() ) {
			if ( got.getId() == prefix ) {
				thisType = got;
				break;
			}
		}
		
		if (thisType == null) return false; //invalid ObjectID
				
		if(thisType.equals(GameObjectType.UNIT)) this.move((ViewUnit)findViewObject(oid), pos);
		else this.move(findViewObject(oid), pos);
		
		return true;
	}
	
	/**
	 * Move a ViewUnit to the specified position with the correct orientation
	 * @param vobj
	 * @param pos
	 */
	public void move(ViewUnit vobj, Position pos){
		Position oldPos = viewObjectGrid.get(vobj);
		int x, y;
		x = pos.getX() - oldPos.getX();
		y = pos.getY() - oldPos.getY();
		
		if(x > 0 && y == 0)
			vobj.setOrientation(MoveDirection.E);
		else if(x > 0 && y > 0)
			vobj.setOrientation(MoveDirection.SE);
		else if(x > 0 && y < 0)
			vobj.setOrientation(MoveDirection.NE);
		else if(x < 0 && y == 0)
			vobj.setOrientation(MoveDirection.W);
		else if(x < 0 && y > 0)
			vobj.setOrientation(MoveDirection.SW);
		else if(x < 0 && y < 0)
			vobj.setOrientation(MoveDirection.NW);
		else if(x == 0 && y < 0)
			vobj.setOrientation(MoveDirection.N);
		else
			vobj.setOrientation(MoveDirection.S);
		
		viewObjectGrid.remove(vobj);
		viewObjectGrid.put(vobj, pos);
	}
	
	/**
	 * Removes the specified ViewObject from the ViewMap.
	 * 
	 * precondition: there is a ViewObject vobj in the ViewMap
	 * postconditon: that ViewObject is removed from the map
	 * 
	 * @param vobj
	 * @return Position of object that was removed
	 */
	public Position remove(ViewObject vobj){
		return viewObjectGrid.remove(vobj);
	}
	
	/**
	 * Removes the ViewObject with the given ObjectID from the ViewMap
	 * 
	 * precondition: there is a ViewObject with the given ID in the ViewMap
	 * postconditon: that ViewObject is removed from the map
	 * 
	 * @param oid
	 * @return Position of the object that was removed
	 */
	public Position remove(ObjectID oid)
	{
		return this.remove(findViewObject(oid));
	}
	
	/**
	 * Clears the map so that it displays just the map itself.
	 *
	 */
	public void clearMap(){
		viewObjectGrid.clear();
	}
	
	/**
	 * Sets the selected tile.
	 * @param pos
	 */
	public void setSelectedTile(Position pos){
		this.selectedTile = pos;
	}
	
	/**
	 * add the specified tile to be highlighted
	 * @param p
	 */
	public void addHighlightedTile(Position p){
		highlightedTiles.add(p);
	}
	
	/**
	 * Returns the highlighted Tiles.
	 * @return
	 */
	public List<Position> getHighlightedTiles(){
		return highlightedTiles;
	}
	
	/**
	 * Clear all the highlighted tiles.
	 */
	public void clearHighlightedTiles(){
		highlightedTiles.clear();
	}
	
	/**
	 * Returns the selected position.
	 * @return
	 */
	public Position getSelectedTile(){
		return this.selectedTile;
	}
	
	public ViewObject findViewObject(ObjectID oid)
	{		
		for(ViewObject current : viewObjectGrid.keySet())
		{
			if (current.objectID().equals(oid)) return current;
		}
		
		return null;
	}
	
	public Position findViewObjectPosition(ObjectID oid)
	{
		for(ViewObject current : viewObjectGrid.keySet())
		{
			if (current.objectID().equals(oid)) return viewObjectGrid.get(current);
		}
		
		return null;
	}

	/**
	 * Return the terrain grid.
	 * @return
	 */
	public Hashtable<ViewTerrain, Position> getTerrainGrid() {
		return terrainGrid;
	}

	/**
	 * returns the View Object Grid that maps ViewObjects to their Positions.
	 * @return
	 */
	public Hashtable<ViewObject, Position> getViewObjectGrid() {
		return viewObjectGrid;
	}

	public Vector<Base> getStructuresForPlayer() {
		return dataHandler.getPlayersBases(currentPlayer);
	}
	
	public void addSelectedObjectID(ObjectID oid){
		objectIDS.add(oid);
	}
	public void clearSelectedObjectID(){
		objectIDS.clear();
	}
	public static ObjectID[] getSelectedObjectIDS(){
		Object[] oids = objectIDS.toArray();
		ObjectID[] returnOids = new ObjectID[oids.length];
		for(int i = 0; i < returnOids.length; i++){
			returnOids[i] = (ObjectID) oids[i];
		}
		return returnOids;
	}
}
